package com.xy.tool.gid.service.impl;

import com.xy.tool.gid.autoconfigure.SnowflakeProperties;
import com.xy.tool.gid.dao.GidNodeDao;
import com.xy.tool.gid.domain.GidNode;
import com.xy.tool.gid.service.GidNodeService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by zsp on 2019/1/22.
 */
@Service
public class GidNodeServiceImpl implements GidNodeService, InitializingBean {

    private int fetchSize;
    private final SecureRandom random = new SecureRandom();

    @Autowired
    private GidNodeDao gidNodeDao;

    @Autowired
    private SnowflakeProperties properties;

    @Override
    public void afterPropertiesSet() throws Exception {
        float factor = properties.getEmptySlotFetchFactor();
        fetchSize = Math.round(Runtime.getRuntime().availableProcessors() * factor);
    }

    @Override
    public void init() {
        List<GidNode> nodeList = new ArrayList<>();
        GidNode node;
        int maxSize = 1 << properties.getServerSequenceBits();
        for (int i = 0; i < maxSize; i++) {
            node = new GidNode();
            node.setIp("");
            node.setPort(0);
            node.setSequence(i);
            nodeList.add(node);
        }
        gidNodeDao.saveAllNode(nodeList);
    }

    @Override
    public Integer getSequenceOrUpdateEmptyNode(String ip, Integer port) {
        Integer sequence = gidNodeDao.getSequenceByIpAndPort(ip, port);
        if (sequence == null) {
            int count = gidNodeDao.countNode();
            if(count == 0) {
                //未初始化节点槽的数据，则返回一个随机数。
                return random.nextInt(GidNode.MAX_SERVER_SIZE);
            }
            List<GidNode> emptyNodeList;
            int index, affectedRows = 0;
            Set<Integer> indexSet = new HashSet<>();
            Outter: for(int i = 0; i < 3; i++) {
                emptyNodeList = gidNodeDao.listEmptyNode(fetchSize);
                for (int j = 0; j < 3; j++) {
                    do {
                        index = random.nextInt(emptyNodeList.size() + 1) - 1;
                        if (index < 0) {
                            index = 0;
                        }
                    } while (indexSet.contains(index));
                    indexSet.add(index);
                    sequence = emptyNodeList.get(index).getSequence();
                    affectedRows = gidNodeDao.updateEmptyNode(sequence, ip, port);
                    if (affectedRows > 0) {
                        break Outter;
                    }
                }
                indexSet.clear();
            }
            if(affectedRows < 1) {
                throw new IllegalStateException("抢占服务节点序号失败");
            }
        }
        return sequence;
    }

    @Override
    public List<GidNode> listEmptyNode(int size) {
        return gidNodeDao.listEmptyNode(size);
    }

}
